package per.liu.service;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import per.liu.dao.DealDao;
import per.liu.dao.UserDao;
import per.liu.model.pojo.Deal;
import per.liu.model.pojo.User;

import java.lang.annotation.*;
import java.util.Date;

/**
 * Created by Administrator on 2015/2/11.
 */
@Service
@DealService.ExecuteResult("这里的spring事务使用的是DataSourceTransactionManager,对某些事务传播行为支持不全面，因此结果也就考虑它是相对" +
		"DataSourceTransactionManager而言的，不具有全面性")
public class DealService {
	@Autowired
	private DealDao dealDao;
	@Autowired
	private UserDao userDao;

	public User findUser(Integer id) {
		return userDao.findById(id);
	}

	public Deal findDeal(Integer id) {
		return dealDao.findById(id);
	}

	@ExecuteResult("udpateDeal方法不会抛出异常，它会改变两个表，一个时user表的geted字段会减少，deal表的op_time会变成最新值")
	public void updateDeal(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		deal.setOpTime(new DateTime());
		dealDao.update(deal);
	}

	@ExecuteResult("user和deal两个表的记录都被回滚了")
	public void updateDealExceptionAtLast(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		deal.setOpTime(new DateTime());
		dealDao.update(deal);
		throw new RuntimeException("test transaction");
	}

	@ExecuteResult("user表的记录都回滚了，deal表的操作之前就已经异常了，因此deal表本来就没变")
	public void updateDealExceptionAtCenter(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		if (true)
			throw new RuntimeException("test transaction");
		deal.setOpTime(new DateTime());
		dealDao.update(deal);
	}

	/**
	 * dao层的advice里面配置了这一条，会影响到user和deal的update操作
	 * <tx:method name="update*" read-only="false" propagation="REQUIRES_NEW"/>
	 * 在这种情况下，dao里面的异常不影响当前事务才对，也就是userDao的提交会成功
	 *
	 * @param user
	 * @param deal
	 */
	@ExecuteResult("userDao.update(user)的确成功的执行了，dealDao的事务和userDao的事务不是同一个事务,同时，事务里面的REQUIRES_NEW事务异常了，是不会影响外面事务提交的")
	public void updateDealTestREQUIRES_NEWTransactionInDaoException(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		deal.setOpTime(new DateTime());
		dealDao.update(deal);
	}

	/**
	 * dao层的advice里面配置了这一条，会影响到user和deal的update操作
	 * <tx:method name="update*" read-only="false" propagation="REQUIRES_NEW"/>
	 * 在这种情况下，dao里面的异常不影响当前事务才对，也就是userDao的提交会成功
	 *
	 * @param user
	 * @param deal
	 */
	@ExecuteResult("userDao.update(user)的确成功的执行了，dealDao的事务和userDao的事务不是同一个事务,同时，事务里面的REQUIRES_NEW事务异常了，是不会影响外面事务提交的")
	public void updateDealTestREQUIRES_NEWTransactionInDaoException2(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		deal.setOpTime(new DateTime());
		dealDao.update(deal);
	}

	/**
	 * dao层的advice里面配置了这一条，会影响到user和deal的update操作
	 * <tx:method name="update*" read-only="false" propagation="NESTED"/>
	 *
	 * @param user
	 * @param deal
	 */
	@ExecuteResult("在nested的传播行为中，由于updateDealTestNestTransactionInDaoException率先开启了一个事务，因此userDao和dealDao会继续使用这个事务" +
			"，测试的结果是userDao被回滚了")
	public void updateDealTestNestTransactionInDaoException(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		deal.setOpTime(new DateTime());
		dealDao.update(null);
	}

	/**
	 * dao层的advice里面配置了这一条，会影响到user和deal的update操作
	 * <tx:method name="update*" read-only="false" propagation="NESTED"/>
	 * 在service层的advice配置一个
	 * <tx:method name="update*" read-only="false" propagation="REQUIRED" no-rollback-for="java.lang.Exception"/>
	 *
	 * @param user
	 * @param deal
	 */
	@ExecuteResult("在nested的传播行为中，由于updateDealTestNestTransactionInDaoException率先开启了一个事务，因此userDao和dealDao会继续使用这个事务" +
			"，由于对service层的rollback进行了定义，使他在java.lang.Exception面前不rollback，最终导致了userDao提交成功")
	public void updateDealTestNestTransactionInDaoException2(User user, Deal deal) {
		user.setGeted(user.getGeted() - deal.getPrice());
		userDao.update(user);
		deal.setOpTime(new DateTime());
		dealDao.update(null);
	}

	@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
	@Retention(RetentionPolicy.SOURCE)
	@Documented
	public static @interface ExecuteResult {
		String value();
	}

}
